{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "ioaprt5q5US7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "yCl0eTNH5RS3"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# 电影评论文本分类"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hKY4XMc9o8iB"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>     <a target=\"_blank\" href=\"https://tensorflow.google.cn/tutorials/keras/text_classification\"><img src=\"https://tensorflow.google.cn/images/tf_logo_32px.png\"> 在 TensorFlow.org 上查看</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/zh-cn/tutorials/keras/text_classification.ipynb\"><img src=\"https://tensorflow.google.cn/images/colab_logo_32px.png\">在 Google Colab 中运行</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/tutorials/keras/text_classification.ipynb\"><img src=\"https://tensorflow.google.cn/images/GitHub-Mark-32px.png\">在 GitHub 上查看源代码</a>   </td>\n",
        "  <td>     <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/zh-cn/tutorials/keras/text_classification.ipynb\"><img src=\"https://tensorflow.google.cn/images/download_logo_32px.png\">下载笔记本</a> </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Eg62Pmz3o83v"
      },
      "source": [
        "本教程演示了从存储在磁盘上的纯文本文件开始的文本分类。您将训练一个二元分类器对 IMDB 数据集执行情感分析。在笔记本的最后，有一个练习供您尝试，您将在其中训练一个多类分类器来预测 Stack Overflow 上编程问题的标签。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "8RZOuS9LWQvv"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import os\n",
        "import re\n",
        "import shutil\n",
        "import string\n",
        "import tensorflow as tf\n",
        "\n",
        "from tensorflow.keras import layers\n",
        "from tensorflow.keras import losses\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "6-tTFS04dChr",
        "outputId": "b264b3df-c187-45e3-87f2-6562b510e2d9",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "2.15.0\n"
          ]
        }
      ],
      "source": [
        "print(tf.__version__)"
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "BOMLGUg9x8P9"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NBTI1bi8qdFV"
      },
      "source": [
        "## 情感分析\n",
        "\n",
        "此笔记本训练了一个情感分析模型，利用评论文本将电影评论分类为*正面*或*负面*评价。这是一个*二元*（或二类）分类示例，也是一个重要且应用广泛的机器学习问题。\n",
        "\n",
        "您将使用 [Large Movie Review Dataset](https://ai.stanford.edu/~amaas/data/sentiment/)，其中包含 [Internet Movie Database](https://www.imdb.com/) 中的 50,000 条电影评论文本 。我们将这些评论分为两组，其中 25,000 条用于训练，另外 25,000 条用于测试。训练集和测试集是*均衡的*，也就是说其中包含相等数量的正面评价和负面评价。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iAsKG535pHep"
      },
      "source": [
        "### 下载并探索 IMDB 数据集\n",
        "\n",
        "我们下载并提取数据集，然后浏览一下目录结构。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "k7ZYnuajVlFN",
        "outputId": "32cc25aa-2995-4ff6-982e-c44ba0dac49e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Downloading data from https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\n",
            "84125825/84125825 [==============================] - 2s 0us/step\n"
          ]
        }
      ],
      "source": [
        "url = \"https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\"\n",
        "\n",
        "dataset = tf.keras.utils.get_file(\"aclImdb_v1\", url,\n",
        "                                    untar=True, cache_dir='.',\n",
        "                                    cache_subdir='')\n",
        "\n",
        "dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "355CfOvsV1pl",
        "outputId": "c46bb5b9-2481-4462-943b-c1deeb0e3277",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['imdb.vocab', 'train', 'README', 'test', 'imdbEr.txt']"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ],
      "source": [
        "os.listdir(dataset_dir)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "7ASND15oXpF1",
        "outputId": "9057bd3c-fc41-4dfb-879d-7ada4c591399",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['urls_neg.txt',\n",
              " 'neg',\n",
              " 'urls_unsup.txt',\n",
              " 'unsupBow.feat',\n",
              " 'unsup',\n",
              " 'labeledBow.feat',\n",
              " 'pos',\n",
              " 'urls_pos.txt']"
            ]
          },
          "metadata": {},
          "execution_count": 5
        }
      ],
      "source": [
        "train_dir = os.path.join(dataset_dir, 'train')\n",
        "os.listdir(train_dir)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ysMNMI1CWDFD"
      },
      "source": [
        "`aclImdb/train/pos` 和 `aclImdb/train/neg` 目录包含许多文本文件，每个文件都是一条电影评论。我们来看看其中的一条评论。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "id": "R7g8hFvzWLIZ",
        "outputId": "8299ee2d-72d2-4877-f194-9e43e8b96234",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Rachel Griffiths writes and directs this award winning short film. A heartwarming story about coping with grief and cherishing the memory of those we've loved and lost. Although, only 15 minutes long, Griffiths manages to capture so much emotion and truth onto film in the short space of time. Bud Tingwell gives a touching performance as Will, a widower struggling to cope with his wife's death. Will is confronted by the harsh reality of loneliness and helplessness as he proceeds to take care of Ruth's pet cow, Tulip. The film displays the grief and responsibility one feels for those they have loved and lost. Good cinematography, great direction, and superbly acted. It will bring tears to all those who have lost a loved one, and survived.\n"
          ]
        }
      ],
      "source": [
        "sample_file = os.path.join(train_dir, 'pos/1181_9.txt')\n",
        "with open(sample_file) as f:\n",
        "  print(f.read())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Mk20TEm6ZRFP"
      },
      "source": [
        "### 加载数据集\n",
        "\n",
        "接下来，您将从磁盘加载数据并将其准备为适合训练的格式。为此，您将使用有用的 [text_dataset_from_directory](https://tensorflow.google.cn/api_docs/python/tf/keras/preprocessing/text_dataset_from_directory) 实用工具，它期望的目录结构如下所示。\n",
        "\n",
        "```\n",
        "main_directory/\n",
        "...class_a/\n",
        "......a_text_1.txt\n",
        "......a_text_2.txt\n",
        "...class_b/\n",
        "......b_text_1.txt\n",
        "......b_text_2.txt\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nQauv38Lnok3"
      },
      "source": [
        "要准备用于二元分类的数据集，磁盘上需要有两个文件夹，分别对应于 `class_a` 和 `class_b`。这些将是正面和负面的电影评论，可以在 `aclImdb/train/pos` 和 `aclImdb/train/neg` 中找到。由于 IMDB 数据集包含其他文件夹，因此您需要在使用此实用工具之前将其移除。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "VhejsClzaWfl"
      },
      "outputs": [],
      "source": [
        "remove_dir = os.path.join(train_dir, 'unsup')\n",
        "shutil.rmtree(remove_dir)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "95kkUdRoaeMw"
      },
      "source": [
        "接下来，您将使用 `text_dataset_from_directory` 实用工具创建带标签的 `tf.data.Dataset`。[tf.data](https://tensorflow.google.cn/guide/data) 是一组强大的数据处理工具。\n",
        "\n",
        "运行机器学习实验时，最佳做法是将数据集拆成三份：[训练](https://developers.google.com/machine-learning/glossary#training_set)、[验证](https://developers.google.com/machine-learning/glossary#validation_set) 和 [测试](https://developers.google.com/machine-learning/glossary#test-set)。\n",
        "\n",
        "IMDB 数据集已经分成训练集和测试集，但缺少验证集。我们来通过下面的 `validation_split` 参数，使用 80:20 拆分训练数据来创建验证集。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "id": "nOrK-MTYaw3C",
        "outputId": "0d50515a-bc4e-4feb-c3c9-67967bd103b1",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Found 25000 files belonging to 2 classes.\n",
            "Using 20000 files for training.\n"
          ]
        }
      ],
      "source": [
        "batch_size = 32\n",
        "seed = 42\n",
        "\n",
        "raw_train_ds = tf.keras.utils.text_dataset_from_directory(\n",
        "    'aclImdb/train',\n",
        "    batch_size=batch_size,\n",
        "    validation_split=0.2,\n",
        "    subset='training',\n",
        "    seed=seed)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5Y33oxOUpYkh"
      },
      "source": [
        "如上所示，训练文件夹中有 25,000 个样本，您将使用其中的 80%（或 20,000 个）进行训练。稍后您将看到，您可以通过将数据集直接传递给 `model.fit` 来训练模型。如果您不熟悉 `tf.data`，还可以遍历数据集并打印出一些样本，如下所示。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "id": "51wNaPPApk1K",
        "outputId": "d05a08ef-ba37-476f-cbba-bf181446f753",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Review b'\"Pandemonium\" is a horror movie spoof that comes off more stupid than funny. Believe me when I tell you, I love comedies. Especially comedy spoofs. \"Airplane\", \"The Naked Gun\" trilogy, \"Blazing Saddles\", \"High Anxiety\", and \"Spaceballs\" are some of my favorite comedies that spoof a particular genre. \"Pandemonium\" is not up there with those films. Most of the scenes in this movie had me sitting there in stunned silence because the movie wasn\\'t all that funny. There are a few laughs in the film, but when you watch a comedy, you expect to laugh a lot more than a few times and that\\'s all this film has going for it. Geez, \"Scream\" had more laughs than this film and that was more of a horror film. How bizarre is that?<br /><br />*1/2 (out of four)'\n",
            "Label 0\n",
            "Review b\"David Mamet is a very interesting and a very un-equal director. His first movie 'House of Games' was the one I liked best, and it set a series of films with characters whose perspective of life changes as they get into complicated situations, and so does the perspective of the viewer.<br /><br />So is 'Homicide' which from the title tries to set the mind of the viewer to the usual crime drama. The principal characters are two cops, one Jewish and one Irish who deal with a racially charged area. The murder of an old Jewish shop owner who proves to be an ancient veteran of the Israeli Independence war triggers the Jewish identity in the mind and heart of the Jewish detective.<br /><br />This is were the flaws of the film are the more obvious. The process of awakening is theatrical and hard to believe, the group of Jewish militants is operatic, and the way the detective eventually walks to the final violent confrontation is pathetic. The end of the film itself is Mamet-like smart, but disappoints from a human emotional perspective.<br /><br />Joe Mantegna and William Macy give strong performances, but the flaws of the story are too evident to be easily compensated.\"\n",
            "Label 0\n",
            "Review b'Great documentary about the lives of NY firefighters during the worst terrorist attack of all time.. That reason alone is why this should be a must see collectors item.. What shocked me was not only the attacks, but the\"High Fat Diet\" and physical appearance of some of these firefighters. I think a lot of Doctors would agree with me that,in the physical shape they were in, some of these firefighters would NOT of made it to the 79th floor carrying over 60 lbs of gear. Having said that i now have a greater respect for firefighters and i realize becoming a firefighter is a life altering job. The French have a history of making great documentary\\'s and that is what this is, a Great Documentary.....'\n",
            "Label 1\n"
          ]
        }
      ],
      "source": [
        "for text_batch, label_batch in raw_train_ds.take(1):\n",
        "  for i in range(3):\n",
        "    print(\"Review\", text_batch.numpy()[i])\n",
        "    print(\"Label\", label_batch.numpy()[i])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JWq1SUIrp1a-"
      },
      "source": [
        "请注意，评论包含原始文本（带有标点符号和偶尔出现的 HTML 代码，如 `<br/>`）。我们将在以下部分展示如何处理这些问题。\n",
        "\n",
        "标签为 0 或 1。要查看它们与正面和负面电影评论的对应关系，可以查看数据集上的 `class_names` 属性。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "id": "MlICTG8spyO2",
        "outputId": "ac753250-3683-4c6e-c437-7b5ed1fa94cd",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Label 0 corresponds to neg\n",
            "Label 1 corresponds to pos\n"
          ]
        }
      ],
      "source": [
        "print(\"Label 0 corresponds to\", raw_train_ds.class_names[0])\n",
        "print(\"Label 1 corresponds to\", raw_train_ds.class_names[1])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pbdO39vYqdJr"
      },
      "source": [
        "接下来，您将创建验证数据集和测试数据集。您将使用训练集中剩余的 5,000 条评论进行验证。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SzxazN8Hq1pF"
      },
      "source": [
        "注：使用 `validation_split` 和 `subset` 参数时，请确保要么指定随机种子，要么传递 `shuffle=False`，这样验证拆分和训练拆分就不会重叠。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "id": "JsMwwhOoqjKF",
        "outputId": "bade7e77-0f2b-4f5c-b21f-a5d071e9a041",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Found 25000 files belonging to 2 classes.\n",
            "Using 5000 files for validation.\n"
          ]
        }
      ],
      "source": [
        "raw_val_ds = tf.keras.utils.text_dataset_from_directory(\n",
        "    'aclImdb/train',\n",
        "    batch_size=batch_size,\n",
        "    validation_split=0.2,\n",
        "    subset='validation',\n",
        "    seed=seed)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "id": "rdSr0Nt3q_ns",
        "outputId": "454cca50-73c2-44dd-83b9-3ef0f2d1e899",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Found 25000 files belonging to 2 classes.\n"
          ]
        }
      ],
      "source": [
        "raw_test_ds = tf.keras.utils.text_dataset_from_directory(\n",
        "    'aclImdb/test',\n",
        "    batch_size=batch_size)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qJmTiO0IYAjm"
      },
      "source": [
        "### 准备用于训练的数据集\n",
        "\n",
        "接下来，您将使用有用的 `tf.keras.layers.TextVectorization` 层对数据进行标准化、词例化和向量化。\n",
        "\n",
        "标准化是指对文本进行预处理，通常是移除标点符号或 HTML 元素以简化数据集。词例化是指将字符串分割成词例（例如，通过空格将句子分割成单个单词）。向量化是指将词例转换为数字，以便将它们输入神经网络。所有这些任务都可以通过这个层完成。\n",
        "\n",
        "正如您在上面看到的，评论包含各种 HTML 代码，例如 `<br />`。`TextVectorization` 层（默认情况下会将文本转换为小写并去除标点符号，但不会去除 HTML）中的默认标准化程序不会移除这些代码。您将编写一个自定义标准化函数来移除 HTML。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZVcHl-SLrH-u"
      },
      "source": [
        "注：为了防止[训练-测试偏差](https://developers.google.com/machine-learning/guides/rules-of-ml#training-serving_skew)（也称为训练-应用偏差），在训练和测试时间对数据进行相同的预处理非常重要。为此，可以将 `TextVectorization` 层直接包含在模型中，如本教程后面所示。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "id": "SDRI_s_tX1Hk"
      },
      "outputs": [],
      "source": [
        "def custom_standardization(input_data):\n",
        "  lowercase = tf.strings.lower(input_data)\n",
        "  stripped_html = tf.strings.regex_replace(lowercase, '<br />', ' ')\n",
        "  return tf.strings.regex_replace(stripped_html,\n",
        "                                  '[%s]' % re.escape(string.punctuation),\n",
        "                                  '')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d2d3Aw8dsUux"
      },
      "source": [
        "<br>接下来，您将创建一个 `TextVectorization` 层。您将使用该层对我们的数据进行标准化、词例化和向量化。您将 `output_mode` 设置为 `int` 以便为每个词例创建唯一的整数索引。\n",
        "\n",
        "请注意，您使用的是默认拆分函数，以及您在上面定义的自定义标准化函数。您还将为模型定义一些常量，例如显式的最大 `sequence_length`，这会使层将序列填充或截断为精确的 `sequence_length` 值。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "id": "-c76RvSzsMnX"
      },
      "outputs": [],
      "source": [
        "max_features = 10000\n",
        "sequence_length = 250\n",
        "\n",
        "vectorize_layer = layers.TextVectorization(\n",
        "    standardize=custom_standardization,\n",
        "    max_tokens=max_features,\n",
        "    output_mode='int',\n",
        "    output_sequence_length=sequence_length)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vlFOpfF6scT6"
      },
      "source": [
        "接下来，您将调用 `adapt` 以使预处理层的状态适合数据集。这会使模型构建字符串到整数的索引。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lAhdjK7AtroA"
      },
      "source": [
        "注：在调用时请务必仅使用您的训练数据（使用测试集会泄漏信息）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "id": "GH4_2ZGJsa_X"
      },
      "outputs": [],
      "source": [
        "# Make a text-only dataset (without labels), then call adapt\n",
        "train_text = raw_train_ds.map(lambda x, y: x)\n",
        "vectorize_layer.adapt(train_text)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SHQVEFzNt-K_"
      },
      "source": [
        "我们来创建一个函数来查看使用该层预处理一些数据的结果。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "id": "SCIg_T50wOCU"
      },
      "outputs": [],
      "source": [
        "def vectorize_text(text, label):\n",
        "  text = tf.expand_dims(text, -1)\n",
        "  return vectorize_layer(text), label"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "id": "XULcm6B3xQIO",
        "outputId": "91d96abb-2fda-4f4d-b3c0-69d8854af560",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Review tf.Tensor(b'Great movie - especially the music - Etta James - \"At Last\". This speaks volumes when you have finally found that special someone.', shape=(), dtype=string)\n",
            "Label neg\n",
            "Vectorized review (<tf.Tensor: shape=(1, 250), dtype=int64, numpy=\n",
            "array([[  86,   17,  260,    2,  222,    1,  571,   31,  229,   11, 2418,\n",
            "           1,   51,   22,   25,  404,  251,   12,  306,  282,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,\n",
            "           0,    0,    0,    0,    0,    0,    0,    0]])>, <tf.Tensor: shape=(), dtype=int32, numpy=0>)\n"
          ]
        }
      ],
      "source": [
        "# retrieve a batch (of 32 reviews and labels) from the dataset\n",
        "text_batch, label_batch = next(iter(raw_train_ds))\n",
        "first_review, first_label = text_batch[0], label_batch[0]\n",
        "print(\"Review\", first_review)\n",
        "print(\"Label\", raw_train_ds.class_names[first_label])\n",
        "print(\"Vectorized review\", vectorize_text(first_review, first_label))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6u5EX0hxyNZT"
      },
      "source": [
        "正如您在上面看到的，每个词例都被一个整数替换了。您可以通过在该层上调用 `.get_vocabulary()` 来查找每个整数对应的词例（字符串）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "id": "kRq9hTQzhVhW",
        "outputId": "64233a32-38eb-4f4a-e980-be49fc25a022",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "1287 --->  silent\n",
            " 313 --->  night\n",
            "Vocabulary size: 10000\n"
          ]
        }
      ],
      "source": [
        "print(\"1287 ---> \",vectorize_layer.get_vocabulary()[1287])\n",
        "print(\" 313 ---> \",vectorize_layer.get_vocabulary()[313])\n",
        "print('Vocabulary size: {}'.format(len(vectorize_layer.get_vocabulary())))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XD2H6utRydGv"
      },
      "source": [
        "你几乎已经准备好训练你的模型了。作为最后的预处理步骤，你将在训练、验证和测试数据集上应用之前创建的TextVectorization层。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "id": "2zhmpeViI1iG"
      },
      "outputs": [],
      "source": [
        "train_ds = raw_train_ds.map(vectorize_text)\n",
        "val_ds = raw_val_ds.map(vectorize_text)\n",
        "test_ds = raw_test_ds.map(vectorize_text)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YsVQyPMizjuO"
      },
      "source": [
        "### 配置数据集以提高性能\n",
        "\n",
        "以下是加载数据时应该使用的两种重要方法，以确保 I/O 不会阻塞。\n",
        "\n",
        "从磁盘加载后，`.cache()` 会将数据保存在内存中。这将确保数据集在训练模型时不会成为瓶颈。如果您的数据集太大而无法放入内存，也可以使用此方法创建高性能的磁盘缓存，这比许多小文件的读取效率更高。\n",
        "\n",
        "`prefetch()` 会在训练时将数据预处理和模型执行重叠。\n",
        "\n",
        "您可以在[数据性能指南](https://tensorflow.google.cn/guide/data_performance)中深入了解这两种方法，以及如何将数据缓存到磁盘。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "wMcs_H7izm5m"
      },
      "outputs": [],
      "source": [
        "AUTOTUNE = tf.data.AUTOTUNE\n",
        "\n",
        "train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)\n",
        "val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)\n",
        "test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LLC02j2g-llC"
      },
      "source": [
        "### 创建模型\n",
        "\n",
        "是时候创建您的神经网络了："
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "id": "dkQP6in8yUBR"
      },
      "outputs": [],
      "source": [
        "embedding_dim = 16"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "id": "xpKOoWgu-llD",
        "outputId": "ee13d05d-93d2-49e4-819f-53aa5f7cb49b",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Model: \"sequential\"\n",
            "_________________________________________________________________\n",
            " Layer (type)                Output Shape              Param #   \n",
            "=================================================================\n",
            " embedding (Embedding)       (None, None, 16)          160016    \n",
            "                                                                 \n",
            " dropout (Dropout)           (None, None, 16)          0         \n",
            "                                                                 \n",
            " global_average_pooling1d (  (None, 16)                0         \n",
            " GlobalAveragePooling1D)                                         \n",
            "                                                                 \n",
            " dropout_1 (Dropout)         (None, 16)                0         \n",
            "                                                                 \n",
            " dense (Dense)               (None, 1)                 17        \n",
            "                                                                 \n",
            "=================================================================\n",
            "Total params: 160033 (625.13 KB)\n",
            "Trainable params: 160033 (625.13 KB)\n",
            "Non-trainable params: 0 (0.00 Byte)\n",
            "_________________________________________________________________\n"
          ]
        }
      ],
      "source": [
        "model = tf.keras.Sequential([\n",
        "  layers.Embedding(max_features + 1, embedding_dim),\n",
        "  layers.Dropout(0.2),\n",
        "  layers.GlobalAveragePooling1D(),\n",
        "  layers.Dropout(0.2),\n",
        "  layers.Dense(1)])\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "层按顺序堆叠以构建分类器：\n",
        "\n",
        "1. 第一个层是 `Embedding` 层。此层采用整数编码的评论，并查找每个单词索引的嵌入向量。这些向量是通过模型训练学习到的。向量向输出数组增加了一个维度。得到的维度为：`(batch, sequence, embedding)`。要详细了解嵌入向量，请参阅[单词嵌入向量](https://tensorflow.google.cn/text/guide/word_embeddings)教程。\n",
        "2. 接下来，`GlobalAveragePooling1D` 将通过对序列维度求平均值来为每个样本返回一个定长输出向量。这允许模型以尽可能最简单的方式处理变长输入。\n",
        "3. 最后一层与单个输出结点密集连接。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### 损失函数与优化器\n",
        "\n",
        "模型训练需要一个损失函数和一个优化器。由于这是一个二元分类问题，并且模型输出概率（具有 Sigmoid 激活的单一单元层），我们将使用 `losses.BinaryCrossentropy` 损失函数。\n",
        "\n",
        "现在，配置模型以使用优化器和损失函数："
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "id": "Mr0GP-cQ-llN"
      },
      "outputs": [],
      "source": [
        "model.compile(loss=losses.BinaryCrossentropy(from_logits=True),\n",
        "              optimizer='adam',\n",
        "              metrics=tf.metrics.BinaryAccuracy(threshold=0.0))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "35jv_fzP-llU"
      },
      "source": [
        "### 训练模型\n",
        "\n",
        "将 `dataset` 对象传递给 fit 方法，对模型进行训练。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "id": "tXSGrjWZ-llW",
        "outputId": "1174caf8-1d0e-4cf0-b642-3272514eca39",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Epoch 1/10\n",
            "625/625 [==============================] - 52s 79ms/step - loss: 0.6637 - binary_accuracy: 0.6955 - val_loss: 0.6129 - val_binary_accuracy: 0.7742\n",
            "Epoch 2/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.5454 - binary_accuracy: 0.8030 - val_loss: 0.4953 - val_binary_accuracy: 0.8232\n",
            "Epoch 3/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.4424 - binary_accuracy: 0.8453 - val_loss: 0.4183 - val_binary_accuracy: 0.8466\n",
            "Epoch 4/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.3770 - binary_accuracy: 0.8655 - val_loss: 0.3726 - val_binary_accuracy: 0.8598\n",
            "Epoch 5/10\n",
            "625/625 [==============================] - 3s 5ms/step - loss: 0.3341 - binary_accuracy: 0.8783 - val_loss: 0.3443 - val_binary_accuracy: 0.8674\n",
            "Epoch 6/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.3043 - binary_accuracy: 0.8886 - val_loss: 0.3257 - val_binary_accuracy: 0.8704\n",
            "Epoch 7/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.2805 - binary_accuracy: 0.8974 - val_loss: 0.3122 - val_binary_accuracy: 0.8746\n",
            "Epoch 8/10\n",
            "625/625 [==============================] - 2s 4ms/step - loss: 0.2616 - binary_accuracy: 0.9053 - val_loss: 0.3032 - val_binary_accuracy: 0.8752\n",
            "Epoch 9/10\n",
            "625/625 [==============================] - 3s 6ms/step - loss: 0.2451 - binary_accuracy: 0.9102 - val_loss: 0.2967 - val_binary_accuracy: 0.8778\n",
            "Epoch 10/10\n",
            "625/625 [==============================] - 3s 4ms/step - loss: 0.2308 - binary_accuracy: 0.9176 - val_loss: 0.2919 - val_binary_accuracy: 0.8786\n"
          ]
        }
      ],
      "source": [
        "epochs = 10\n",
        "history = model.fit(\n",
        "    train_ds,\n",
        "    validation_data=val_ds,\n",
        "    epochs=epochs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "### 评估模型\n",
        "\n",
        "我们来看一下模型的性能如何。将返回两个值。损失值（loss）（一个表示误差的数字，值越低越好）与准确率（accuracy）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "id": "zOMKywn4zReN",
        "outputId": "2e4b3a9c-b488-4841-8292-1a397e7e5e78",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "782/782 [==============================] - 3s 4ms/step - loss: 0.3103 - binary_accuracy: 0.8734\n",
            "Loss:  0.3103073537349701\n",
            "Accuracy:  0.8733999729156494\n"
          ]
        }
      ],
      "source": [
        "loss, accuracy = model.evaluate(test_ds)\n",
        "\n",
        "print(\"Loss: \", loss)\n",
        "print(\"Accuracy: \", accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "这种十分简单的方式实现了约 86% 的准确率。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ldbQqCw2Xc1W"
      },
      "source": [
        "### 创建准确率和损失随时间变化的图表\n",
        "\n",
        "`model.fit()` 会返回包含一个字典的 `History` 对象。该字典包含训练过程中产生的所有信息："
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {
        "id": "-YcvZsdvWfDf",
        "outputId": "2bf1f8b1-6952-4b44-96fc-d37e0f12d673",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "dict_keys(['loss', 'binary_accuracy', 'val_loss', 'val_binary_accuracy'])"
            ]
          },
          "metadata": {},
          "execution_count": 27
        }
      ],
      "source": [
        "history_dict = history.history\n",
        "history_dict.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1_CH32qJXruI"
      },
      "source": [
        "其中有四个条目：每个条目代表训练和验证过程中的一项监测指标。您可以使用这些指标来绘制用于比较的训练损失和验证损失图表，以及训练准确率和验证准确率图表："
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "id": "2SEMeQ5YXs8z",
        "outputId": "8144e9fd-4e49-4c5d-d95b-4f5c3e86b356",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 472
        }
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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UKBAAYYNG5bma9x6rerVq5M/f37HtoYNG5KQkMDevXsd2ypXroynp6fjdWhoKCdPnkzTNeLj4zl27BgNGzZ02t6wYUP27NkDmLe+du7cSfny5enbty+rV6927Pf4449z+fJlypQpQ7du3Vi8eDE3btxI1+dML4UbERHJEv7+cOGCNY/09uV99tln+fzzzzl//jxz5syhbNmyNGnSBICJEycydepUBg0axPr169m5cyfNmzfn2rVrWfZ3tWXLFjp27MhDDz3El19+yU8//cTQoUOz9Bq3ypcvn9Nrm81GQkJClp3/nnvu4cCBA4wZM4bLly/zxBNP0L59e8BczXzv3r1Mnz4dPz8/evbsSePGjdPV5ye91OdGRESyhM0GtzQg5GpPPPEE/fr145NPPuGDDz6gR48ejv43mzZtonXr1nTq1Akw+9D88ccfVKpUKU3nrlixIkeOHOH48eOEhoYCsHXrVqd9Nm/eTEREBEOHDnVsO3TokNM+3t7e2O32O15r7ty5XLx40dF6s2nTJjw8PChfvnya6r2TgIAAwsLC2LRpkyMAJl7n1j5IAQEBREdHEx0dTfv27WnRogVnzpyhSJEi+Pn50apVK1q1akWvXr2oUKECu3bt4p577smSGm+ncCMiIm6nQIECREdHM2TIEOLj4+nSpYvjvXLlyrFo0SI2b95M4cKFefPNNzlx4kSaw01UVBR33303nTt3ZuLEicTHxzuFmMRrHD58mE8//ZQ6derw1VdfsXjxYqd9IiMjOXDgADt37qRkyZIULFgwyRDwjh07MmLECDp37szIkSM5deoUffr04amnniI4ODhjfznJePHFFxkxYgRly5alRo0azJkzh507d/Lxxx8D8OabbxIaGkrNmjXx8PBg4cKFhISEUKhQIebOnYvdbqdevXr4+/vz0Ucf4efnR0RERJbVdzvdlhIREbf07LPPcvbsWZo3b+7UP2bYsGHcc889NG/enKZNmxISEkKbNm3SfF4PDw8WL17M5cuXqVu3Ls899xxjx4512ufRRx/l+eefp3fv3tSoUYPNmzfzyiuvOO3Trl07WrRowX333UexYsWSHY7u7+/PqlWrOHPmDHXq1KF9+/Y0a9aMadOmpe8v4w769u3LgAEDeOGFF6hatSorV65k2bJllCtXDjBHfk2YMIHatWtTp04dDh48yPLly/Hw8KBQ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5FqJbg1IYWHZV3NaKNyIiIhIunh63hxOnxtpKiERERFxKWq5ERFxM3a75usR16ZwIyLiRmJikp9peepUzbQsrkO3pURE3ERMjLlG1u2z4x49am6PibGmLpGspnAjIuIG7HazxSa54buJ2/r3N/cTyesUbkRE3MDGjamvZ2QYcOSIuZ9IXqdwIyLiBo4fz9r9RHIzhRsRETcQGpq1+4nkZgo3IiJuoFEjc1RUSis/22zmDLWNGuVsXSLZQeFGRMQNeHqaw70hacBJfD1liua7EdegcCMi4ibatoVFi6BECeftJUua2zXPjbgKTeInIuJG2raF1q01Q7G4NoUbERE34+kJTZtaXYVI9tFtKREREXEpCjciIiLiUhRuRERExKWoz42IiORZdrs6R0tSCjciIpInxcSYi4HeumZWyZLmfD4a1u7edFtKRETynJgYaN8+6WKgR4+a22NirKlLcgeFGxERyVPsdrPFxjCSvpe4rX9/cz9xTwo3IiKSp2zcmLTF5laGAUeOmPuJe1K4ERGRPOX48azdT1yPwo2IiOQpoaFZu5+4HoUbERHJUxo1MkdF3b66eSKbDcLDzf3EPSnciIhInuLpaQ73hqQBJ/H1lCma78adKdyIiEie07YtLFoEJUo4by9Z0tyueW7cmybxExGRPKltW2jdWjMUS1IKNyIikmd5ekLTplZXIbmNbkuJiIiIS1HLjYiIiMW0AGjWUrgRERGxkBYAzXq6LSUiImIRLQCaPRRuRERELKAFQLOPwo2IiIgFtABo9lG4ERERsYAWAM0+CjciIiIW0AKg2UfhRkRExAJaADT7KNyIiIhYQAuAZh+FGxEREYtoAdDsoUn8RERELKQFQLOewo2IiIjFXGUB0NyyjITCjYiIiGRablpGQn1uREREJFNy2zISCjciIiKSYblxGQmFGxEREcmw3LiMhMKNiIiIZFhuXEZC4UZEREQyLDcuI6FwIyIiIhmWG5eRULgRERGRDMuNy0go3IiIiEim5LZlJDSJn4iIiGRablpGQuFGREREskRuWUZCt6VERETEpSjciIiIiEtRuBERERGXonAjIiIiLkXhRkRERFyKwo2IiIi4FIUbERERcSkKNyIiIuJSFG5ERETEpbjdDMWGYQAQHx9vcSUiIiKSVonf24nf46lxu3Bz/vx5AMLDwy2uRERERNLr/PnzBAYG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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "acc = history_dict['binary_accuracy']\n",
        "val_acc = history_dict['val_binary_accuracy']\n",
        "loss = history_dict['loss']\n",
        "val_loss = history_dict['val_loss']\n",
        "\n",
        "epochs = range(1, len(acc) + 1)\n",
        "\n",
        "# \"bo\" is for \"blue dot\"\n",
        "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
        "# b is for \"solid blue line\"\n",
        "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
        "plt.title('Training and validation loss')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "metadata": {
        "id": "Z3PJemLPXwz_",
        "outputId": "d18c410f-0060-4e21-f607-11e3f2a49384",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 472
        }
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
        "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
        "plt.title('Training and validation accuracy')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hFFyCuJoXy7r"
      },
      "source": [
        "在该图表中，虚线代表训练损失和准确率，实线代表验证损失和准确率。\n",
        "\n",
        "请注意，训练损失会逐周期*下降*，而训练准确率则逐周期*上升*。使用梯度下降优化时，这是预期结果，它应该在每次迭代中最大限度减少所需的数量。\n",
        "\n",
        "但是，对于验证损失和准确率来说则不然——它们似乎会在训练转确率之前达到顶点。这是过拟合的一个例子：模型在训练数据上的表现要好于在之前从未见过的数据上的表现。经过这一点之后，模型会过度优化和学习*特定*于训练数据的表示，但无法*泛化*到测试数据。\n",
        "\n",
        "对于这种特殊情况，您可以通过在验证准确率不再增加时直接停止训练来防止过度拟合。一种方式是使用 `tf.keras.callbacks.EarlyStopping` 回调。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-to23J3Vy5d3"
      },
      "source": [
        "## 导出模型\n",
        "\n",
        "在上面的代码中，您在向模型馈送文本之前对数据集应用了 `TextVectorization`。 如果您想让模型能够处理原始字符串（例如，为了简化部署），您可以在模型中包含 `TextVectorization` 层。为此，您可以使用刚刚训练的权重创建一个新模型。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "metadata": {
        "id": "FWXsMvryuZuq",
        "outputId": "19bd2e1c-5cef-4f8b-b81a-0bae5ee893ea",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "782/782 [==============================] - 4s 5ms/step - loss: 0.3103 - accuracy: 0.8734\n",
            "0.8733999729156494\n"
          ]
        }
      ],
      "source": [
        "export_model = tf.keras.Sequential([\n",
        "  vectorize_layer,\n",
        "  model,\n",
        "  layers.Activation('sigmoid')\n",
        "])\n",
        "\n",
        "export_model.compile(\n",
        "    loss=losses.BinaryCrossentropy(from_logits=False), optimizer=\"adam\", metrics=['accuracy']\n",
        ")\n",
        "\n",
        "# Test it with `raw_test_ds`, which yields raw strings\n",
        "loss, accuracy = export_model.evaluate(raw_test_ds)\n",
        "print(accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TwQgoN88LoEF"
      },
      "source": [
        "### 使用新数据进行推断\n",
        "\n",
        "要获得对新样本的预测，只需调用 `model.predict()` 即可。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "metadata": {
        "id": "QW355HH5L49K",
        "outputId": "04389e48-2e10-4ddc-b47a-c64b399587b3",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "1/1 [==============================] - 0s 116ms/step\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[0.61882156],\n",
              "       [0.4412337 ],\n",
              "       [0.35709715]], dtype=float32)"
            ]
          },
          "metadata": {},
          "execution_count": 31
        }
      ],
      "source": [
        "examples = [\n",
        "  \"The movie was great!\",\n",
        "  \"The movie was okay.\",\n",
        "  \"The movie was terrible...\"\n",
        "]\n",
        "\n",
        "export_model.predict(examples)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MaxlpFWpzR6c"
      },
      "source": [
        "将文本预处理逻辑包含在模型中后，您可以导出用于生产的模型，从而简化部署并降低[训练/测试偏差](https://developers.google.com/machine-learning/guides/rules-of-ml#training-serving_skew)的可能性。\n",
        "\n",
        "在选择应用 TextVectorization 层的位置时，需要注意性能差异。在模型之外使用它可以让您在 GPU 上训练时进行异步 CPU 处理和数据缓冲。因此，如果您在 GPU 上训练模型，您应该在开发模型时使用此选项以获得最佳性能，然后在准备好部署时进行切换，在模型中包含 TextVectorization 层。\n",
        "\n",
        "请参阅此[教程](https://tensorflow.google.cn/tutorials/keras/save_and_load)，详细了解如何保存模型。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eSSuci_6nCEG"
      },
      "source": [
        "## 练习：对 Stack Overflow 问题进行多类分类\n",
        "\n",
        "本教程展示了如何在 IMDB 数据集上从头开始训练二元分类器。作为练习，您可以修改此笔记本以训练多类分类器来预测 [Stack Overflow](http://stackoverflow.com/) 上的编程问题的标签。\n",
        "\n",
        "我们已经准备好了一个[数据集](https://storage.googleapis.com/download.tensorflow.org/data/stack_overflow_16k.tar.gz)供您使用，其中包含了几千个发布在 Stack Overflow 上的编程问题（例如，\"How can sort a dictionary by value in Python?\"）。每一个问题都只有一个标签（Python、CSharp、JavaScript 或 Java）。您的任务是将问题作为输入，并预测适当的标签，在本例中为 Python。\n",
        "\n",
        "您将使用的数据集包含从 [BigQuery](https://console.cloud.google.com/marketplace/details/stack-exchange/stack-overflow) 上更大的公共 Stack Overflow 数据集提取的数千个问题，其中包含超过 1700 万个帖子。\n",
        "\n",
        "下载数据集后，您会发现它与您之前使用的 IMDB 数据集具有相似的目录结构：\n",
        "\n",
        "```\n",
        "train/\n",
        "...python/\n",
        "......0.txt\n",
        "......1.txt\n",
        "...javascript/\n",
        "......0.txt\n",
        "......1.txt\n",
        "...csharp/\n",
        "......0.txt\n",
        "......1.txt\n",
        "...java/\n",
        "......0.txt\n",
        "......1.txt\n",
        "```\n",
        "\n",
        "注：为了增加分类问题的难度，编程问题中出现的 Python、CSharp、JavaScript 或 Java 等词已被替换为 *blank*（因为许多问题都包含它们所涉及的语言）。\n",
        "\n",
        "要完成此练习，您应该对此笔记本进行以下修改以使用 Stack Overflow 数据集：\n",
        "\n",
        "1. 在笔记本顶部，将下载 IMDB 数据集的代码更新为下载前面准备好的 [Stack Overflow 数据集](https://storage.googleapis.com/download.tensorflow.org/data/stack_overflow_16k.tar.gz)的代码。由于 Stack Overflow 数据集具有类似的目录结构，因此您不需要进行太多修改。\n",
        "\n",
        "2. 将模型的最后一层修改为 `Dense(4)`，因为现在有四个输出类。\n",
        "\n",
        "3. 编译模型时，将损失更改为 `tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)`。当每个类的标签是整数（在本例中，它们可以是 0、*1*、*2* 或 *3*）时，这是用于多类分类问题的正确损失函数。 此外，将指标更改为 `metrics=['accuracy']`，因为这是一个多类分类问题（`tf.metrics.BinaryAccuracy` 仅用于二元分类器 ）。\n",
        "\n",
        "4. 在绘制随时间变化的准确率时，请将 `binary_accuracy` 和 `val_binary_accuracy` 分别更改为 `accuracy` 和 `val_accuracy`。\n",
        "\n",
        "5. 完成这些更改后，就可以训练多类分类器了。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F0T5SIwSm7uc"
      },
      "source": [
        "## 了解更多信息\n",
        "\n",
        "本教程从头开始介绍了文本分类。要详细了解一般的文本分类工作流程，请查看 Google Developers 提供的[文本分类指南](https://developers.google.com/machine-learning/guides/text-classification/)。\n"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "name": "text_classification.ipynb",
      "toc_visible": true,
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}